home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 4 / Apprentice-Release4.iso / Utilities / Programming / EnterAct 3.5 / Drag_on Modules / hAWK programs / $Multi_Undo < prev    next >
Encoding:
Text File  |  1993-04-09  |  16.9 KB  |  619 lines  |  [TEXT/KEEN]

  1. #$Multi_Undo : for use with EnterAct's "••Recent Activities••"
  2. # listing only, regenerates an earlier version of a file in
  3. # $tempStdOut. Slightly complicated to get going, and subject
  4. # to some limitations, so please read instructions below before
  5. # proceeding.
  6.  
  7. # Let's say you have a file called "A", with full path name
  8. # "Disk:folder1:...:folderN:A" that you've been
  9. # editing, and you wish to undo all the (saved) changes
  10. # you've made to it, back to a specific time or alteration.
  11. # Let's call the point back to which you wish to undo the
  12. # "undo point" for file A.
  13. # This program will do it, provided the circumstances are
  14. # as follows:
  15. #    1    file A currently does not need saving
  16. #    2    All changes to file A, from to the undo point up to
  17. #        the present, have been made with EnterAct
  18. #    3    From the undo point up to the present, no other files
  19. #        named "A" have been edited with EnterAct (ie a different
  20. #        disk or folder, but the same basic file name A)
  21. #    4    The activity corresponding to the undo point is present
  22. #        in EnterAct's "••Recent Activities••" window (it goes
  23. #        back "only" 1024 activities).
  24. #    5    The entire text of each edit on file A is present in
  25. #        "••Recent Activities••": you normally won't want to
  26. #        check for this yourself, and this program will do it
  27. #        thoroughly for you. In general, only inserts or deletes
  28. #        below 32K characters are fully recorded, and older large
  29. #        edits near the beginning of "••Recent Activities••" may
  30. #        not be fully recorded if it was necessary to make room
  31. #        for newer large edits. Rule of thumb: barring a sequence
  32. #        of large edits, all of your edits will still be fully
  33. #        recorded. Don't worry about it.
  34.  
  35. #    If your circumstances meet the above limitations, you can
  36. #    undo changes to a particular file A back to a specific point
  37. #    as follows:
  38. #    1    Open an EnterAct project
  39. #    2    If necessary, add file A to it
  40. #    3    Select "Show Activities..."; click on the "originally
  41. #        recorded positions" button; and for the number of activities
  42. #        to show, enter "1024"; and click the OK button to generate
  43. #        the "••Recent Activities••" window
  44. #    4    Locate the oldest activity you wish undone, and note its
  45. #        activity number -- this appears just afer the "¶" beginning
  46. #        the activity record, for example "¶314"
  47. #    5    Save "••Recent Activities••" somewhere - NOTE the name must
  48. #        start with two bullets, but is otherwise arbitrary, for example,
  49. #        "••RA" or "••For undo"
  50. #    6    Add your saved version of "••Recent Activities••" to your
  51. #        project, then close the window for "••Recent Activities••"
  52. #        to save memory
  53. #    7    Select both the saved version of "••Recent Activities••" and
  54. #        file A for MFS operations (hold down the <Option> key and
  55. #        click on each name in the project window - a bullet • will
  56. #        appear beside the file name). These two should be the only
  57. #        files marked with a • in your project window
  58. #    8    Call up hAWK and select this program; the input option should
  59. #        be "MFS selected files"; use "Set Variables" to set the value
  60. #        of the variable "howFarBack" to the activity number that represents
  61. #        how far back (inclusive) you wish to undo - for example,
  62. #             howFarBack=314
  63. #        -to undo from the present back to and including activity number 314.
  64. #    9    Click the Run button. The undone version of file A will
  65. #        (eventually) be presented in the "$tempStdOut" window.
  66. #    10    If there are other files you wish to restore, add them to your project
  67. #        if necessary and repeat steps 4-9 for each file.
  68. #    NOTE while you're waiting you should avoid editing either
  69. #    file A or your saved version of "••Recent Activities••".
  70.  
  71. #    Whew! Not trivial, but it does work.
  72.  
  73. #    One little bug: a spurious blank line may be appended to the undone
  74. #    version of your file. This results from the way hAWK read lines (in
  75. #    particular, expecting the last line of a file to end with a carriage return)
  76. #    and the fix would be 1)difficult and 2)rather pointless. Let's live with it.
  77.  
  78. ######### EnterAct's activity format #########
  79. #    Generic activity format: '{}' means optional, 'OR' means exclusive or.
  80. #    D stands for digit. Both "line" and "charPos" are D{D}.
  81. #    "timestamp" is eg "Sun, Nov 22, 1992 1:03:07 AM"
  82. #    ¶D{D}
  83. #    timestamp activity «fname» line charPos {(temp) OR (obs) OR (undone)}
  84. #    <=deleted D{D} characters{ (D{D} shown)}:
  85. #    OR
  86. #    =>inserted D{D} characters{ (D{D} shown)}:
  87. #    «data»
  88. #    ¬
  89. #
  90. #    -where "charPos" is the character position of the start of the insert
  91. #    or delete counting from the start of the line, and both "line" and
  92. #    "charPos" are 1-based. Eg line 7 charPos 1 means the insert was just before
  93. #     the first character on the seventh line.
  94. #    -"shown" appears only when activity data is not fully shown.
  95.  
  96. ######### Globals ##########
  97. #    activityFile - full path name of activity file
  98. #    undoFile - full path name of file being reverted
  99. #    quotedUndoName - undo file name proper, inside «»
  100. #    yActivity, yFName, yLine, yCharPos, yClass - set from the
  101. #        "timestamp" line of the activity record
  102. #    ySize, yShownSize - set from "<=deleted"/"=>inserted" line
  103. #    numActivities - number of activities to be undone
  104. #    ASize[] - size of data in bytes for activity
  105. #    ADelete[] - 1 if delete, 0 if insert
  106. #    ALine[]    - starting line (1-based)
  107. #    ACharPos[] - starting character position on line (1-based)
  108. #    AText[] - contents of deletes only
  109. #    AText2[] - overflow delete contents (up to 64K needed, but a hAWK string can
  110. #        only hold up to 32K)
  111. #    AText3[] - ditto
  112. #    ANumCRs[] - number of carriage returns in edit text
  113. #    ALLChars[] - number of characters in last line of edit
  114. #    out[] - contents, line-by-line, of undoFile
  115.  
  116. # User’s Manual references:
  117. # «hAWK User’s Manual» «F   Running hAWK programs»
  118. # «hAWK User’s Manual» «L  5   Regular expressions»
  119. # «hAWK User’s Manual» «M  5   Built-in string and file functions»
  120. # «hAWK User’s Manual» «K  4   Built-in variables»
  121. # «hAWK User’s Manual» «K  8   Arrays»
  122. # «hAWK User’s Manual» «N   User-defined functions»
  123. # «hAWK User’s Manual» «P  3   The getline function»
  124. # «hAWK User’s Manual» «O  3   Output into files»
  125. # «hAWK User’s Manual» «Q   The hAWK function»
  126.  
  127.  
  128. ############ the program ###################
  129. BEGIN {
  130.     permanent = 1
  131.     temporary = 2
  132.     obsolete = 3
  133.     permUndone = 4
  134.     WhichFileIsWhich()
  135.     doingActivityFile = 1
  136.         ##TEST ONLY
  137.         ##DumpStats()
  138.     LoadActivities()
  139.     doingActivityFile = 0
  140.         ##TEST ONLY
  141.         ##DumpActivities()
  142.     LoadUndoFileLines()
  143.     UndoActivities()
  144.     PrintUndoneVersionOfFile()
  145.     }
  146.  
  147. function WhichFileIsWhich(    n,names)
  148.     {
  149.     if (ARGC > 3)
  150.         Error("more than two files specified for input")
  151.     n = split(ARGV[1], names, ":")
  152.     if (match(names[n], /^••/))
  153.         {
  154.         activityFile = ARGV[1];
  155.         undoFile = ARGV[2];
  156.         }
  157.     else
  158.         {
  159.         n = split(ARGV[2], names, ":")
  160.         if (match(names[n], /^••/))
  161.             {
  162.             activityFile = ARGV[2];
  163.             undoFile = ARGV[1];
  164.             }
  165.         else
  166.             Error("\"...••Recent Activities••...\" file not specified")
  167.         }
  168.     n = split(undoFile, names, ":")
  169.     quotedUndoName = "«" names[n] "»"
  170.     }
  171.  
  172. function LoadActivities()
  173.     {
  174.     SkipEarlyActivities()
  175.     GetActivitiesToUndo()
  176.     }
  177.  
  178. function SkipEarlyActivities()
  179.     {
  180.     
  181.     while ((getline < activityFile) > 0)
  182.         {
  183.         if (!match($0, /¶[0-9]+/))
  184.             Error("activity number is unreadable");
  185.         which = substr($0, RSTART+1, RLENGTH-1) + 0;
  186.         if (which < howFarBack)
  187.             {
  188.             GetNextActivityLine() #"timestamp" etc line
  189.             SkipRemainderOfActivity()
  190.             }
  191.         else if (which == howFarBack)
  192.             break;
  193.         else
  194.             Error("activity number " howFarBack " was not found in sequence")
  195.         }
  196.     if (which == howFarBack)
  197.         ; # bufferedLine = $0 -- but this line is not needed after all...
  198.     else
  199.         Error("activity number " howFarBack " was not found in sequence")
  200.     }
  201.  
  202. function SkipRemainderOfActivity()
  203.     {
  204.     # assumption: on "timestamp" etc line at this point
  205.     GetNextActivityLine() #"<=deleted"/"=>inserted"/"¬"/other
  206.     if (match($0, /^¬/))
  207.         return;
  208.     else if (match($0, /^<=deleted/) || match($0, /^=>inserted/))
  209.         {
  210.         GetActivitySizes()
  211.         SkipActivityData()
  212.         }
  213.     GetNextActivityLine()
  214.     if (match($0, /^¬/))
  215.         return;
  216.     else
  217.         Error("unrecognized or missing activity line")
  218.     }
  219.  
  220. function GetNextActivityLine()
  221.     {
  222.     if ((getline < activityFile) <= 0)
  223.         Error("unexpected early end of activity file")
  224.     }
  225.  
  226. function GetActivitySizes()
  227.     {
  228.     match($0, /[0-9]+/)
  229.     ySize = substr($0, RSTART, RLENGTH)+0
  230.     temp = substr($0, RSTART+RLENGTH)
  231.     if (match(temp, /[0-9]+/))
  232.         yShownSize = substr(temp, RSTART, RLENGTH)+0
  233.     else
  234.         yShownSize = ySize;
  235.     }
  236.  
  237. function SkipActivityData()
  238.     {
  239.     yShownSize += 2 # for the start and end quotes
  240.     while (yShownSize > 0)
  241.         {
  242.         GetNextActivityLine()
  243.         yShownSize -= length()
  244.         if (yShownSize > 0)    # not last line of data
  245.             --yShownSize;        # for CR at end of line
  246.         }
  247.     }
  248.  
  249.  
  250. function GetActivitiesToUndo()
  251.     {
  252.     while ((getline < activityFile) > 0) # "timestamp" line
  253.         {
  254.         ParseTimeLine()
  255.         if (yFName == quotedUndoName && yClass == permanent)
  256.             {
  257.             GetNextActivityLine() #"<=deleted"/"=>inserted"/"¬"/other
  258.             if (match($0, /^¬/))
  259.                 {
  260.                 SeeIfAnotherActivity()
  261.                 continue
  262.                 }
  263.             else if (match($0, /^=>inserted/))
  264.                 {
  265.                 GetActivitySizes()
  266.                 if (ySize != yShownSize)
  267.                     Error("activity file is missing data for an insertion")
  268.                 GetNextActivityLine()
  269.                 RecordActivity(0)
  270.                 }
  271.             else if (match($0, /^<=deleted/))
  272.                 {
  273.                 GetActivitySizes()
  274.                 if (ySize != yShownSize)
  275.                     Error("activity file is missing data for a deletion")
  276.                 GetNextActivityLine()
  277.                 RecordActivity(1)
  278.                 }
  279.             GetNextActivityLine()
  280.             if (!match($0, /^¬/))
  281.                 Error("end of activity not found")
  282.             SeeIfAnotherActivity()
  283.             }
  284.         else # skip to next activity
  285.             {
  286.             SkipRemainderOfActivity()
  287.             SeeIfAnotherActivity()
  288.             }
  289.         }
  290.     }
  291.  
  292. #    timestamp activity «fname» line charPos {(temp) OR (obs)}
  293. #    "timestamp" is eg "Sun, Nov 22, 1992 1:03:07 AM"
  294. #    Sets yActivity, yFName, yLine, yCharPos, yClass
  295. function ParseTimeLine()
  296.     {
  297.     if (!match($0, /[APM]+/))
  298.         Error("time stamp is missing from activity")
  299.     $0 = substr($0, RSTART+RLENGTH+1) #skip timestamp and a space
  300.     yActivity = $1
  301.     if (!match($0, /«.*»/))
  302.         {
  303.         yFName = ""
  304.         yLine = yCharPos = 0
  305.         return
  306.         }
  307.     yFName = substr($0, RSTART, RLENGTH)
  308.     $0 = substr($0, RSTART+RLENGTH+1)
  309.     yLine = $1
  310.     yCharPos = $2
  311.     if ($3 == "")
  312.         yClass = permanent
  313.     else if ($3 == "(temp)")
  314.         yClass = temporary
  315.     else if ($3 == "(obs)")
  316.         yClass = obsolete
  317.     else if ($3 == "(undone)")
  318.         yClass = permUndone
  319.     else
  320.         Error("unknown activity class")
  321.     }
  322.  
  323. function SeeIfAnotherActivity()
  324.     {
  325.     if ((getline < activityFile) <= 0)
  326.         return;
  327.     if (!match($0, /¶[0-9]+/))
  328.         Error("expected but did not find ¶ followed by number");
  329.     which = substr($0, RSTART+1, RLENGTH-1) + 0;
  330.     }
  331.  
  332. function RecordActivity(wasADelete,        len, totalLen)
  333.     {
  334.     if (!match($0, /^«/))
  335.         Error("data for an activity is missing")
  336.     ASize[++numActivities] = ySize
  337.     ADelete[numActivities] = wasADelete
  338.     ALine[numActivities] = yLine
  339.     ACharPos[numActivities] = yCharPos
  340.     
  341.     ## debug only
  342.     AWhich[numActivities] = which
  343.     
  344.     yShownSize += 2 # for the start and end quotes
  345.     ANumCRs[numActivities] = 0
  346.     if (yShownSize <= length())
  347.         {
  348.         ALLChars[numActivities] = ySize
  349.         if (wasADelete)
  350.             AText[numActivities] = substr($0, 2, length() - 2)
  351.         return
  352.         }
  353.     if (wasADelete)
  354.         AText[numActivities] = substr($0, 2, length() - 1)
  355.     yShownSize -= length()
  356.     --yShownSize;        # for CR at end of line
  357.     if (wasADelete)
  358.         {
  359.         while (yShownSize > 0)
  360.             {
  361.             GetNextActivityLine()
  362.             len = length()
  363.             yShownSize -= len
  364.             ANumCRs[numActivities]++
  365.             if (yShownSize > 0)    # not last line of data
  366.                 {
  367.                 --yShownSize;        # for CR at end of line
  368.                 totalSize += len + 1
  369.                 if (totalSize > 64000)
  370.                     AText3[numActivities] = AText3[numActivities] "\n" $0
  371.                 else if (totalSize > 32000)
  372.                     AText2[numActivities] = AText2[numActivities] "\n" $0
  373.                 else
  374.                     AText[numActivities] = AText[numActivities] "\n" $0
  375.                 }
  376.             else
  377.                 {
  378.                 --len
  379.                 totalSize += len + 1
  380.                 if (totalSize > 64000)
  381.                     AText3[numActivities] = AText3[numActivities] "\n" substr($0, 1, len)
  382.                 else if (totalSize > 32000)
  383.                     AText2[numActivities] = AText2[numActivities] "\n" substr($0, 1, len)
  384.                 else
  385.                     AText[numActivities] = AText[numActivities] "\n" substr($0, 1, len)
  386.                 ALLChars[numActivities] = len
  387.                 }
  388.             }
  389.         }
  390.     else
  391.         {
  392.         while (yShownSize > 0)
  393.             {
  394.             GetNextActivityLine()
  395.             len = length()
  396.             yShownSize -= len
  397.             ANumCRs[numActivities]++
  398.             if (yShownSize > 0)    # not last line of data
  399.                 --yShownSize;        # for CR at end of line
  400.             else
  401.                 ALLChars[numActivities] = len - 1
  402.             }
  403.         }
  404.     }
  405.  
  406. function LoadUndoFileLines()
  407.     {
  408.     while ((getline < undoFile) > 0)
  409.         out[++outLines] = $0
  410.     ++outLines # to compensate for the way hAWK reads lines - the last line of the
  411.                 # file may not have had a terminating carriage return.
  412.     }
  413.  
  414.  
  415. function UndoActivities(    i)
  416.     {
  417.     for (i = numActivities; i >= 1; --i)
  418.         {
  419.         if (ADelete[i])
  420.             UndoDelete(i)
  421.         else
  422.             UndoInsert(i)
  423.         }
  424.     }
  425.  
  426. #Put the deleted text back. All bookkeeping and arrays are 1-based.
  427. function UndoDelete(x,        i, n, m, lineArr, lineArr2, lineArr3, trailer, startLine, numcrs)
  428.     {
  429.     startLine = ALine[x]
  430.     if (ANumCRs[x] == 0)
  431.         {
  432.         out[startLine] = substr(out[startLine], 1, ACharPos[x] - 1)\
  433.             AText[x] substr(out[startLine], ACharPos[x])
  434.         }
  435.     else
  436.         {
  437.         n = split(AText[x], lineArr, "\n")
  438.         if (x in AText2)
  439.             {
  440.             m = split(AText2[x], lineArr2, "\n")
  441.             for (i = 1; i <= m; ++i)
  442.                 {
  443.                 lineArr[n+i] = lineArr2[i]
  444.                 delete lineArr2[i]
  445.                 }
  446.             if (x in AText3)
  447.                 {
  448.                 n += m
  449.                 m = split(AText3[x], lineArr3, "\n")
  450.                 for (i = 1; i <= m; ++i)
  451.                     {
  452.                     lineArr[n+i] = lineArr3[i]
  453.                     delete lineArr3[i]
  454.                     }
  455.                 }
  456.             }
  457.         n = ANumCRs[x] + 1
  458.         trailer = substr(out[startLine], ACharPos[x])
  459.         numcrs = ANumCRs[x]
  460.         for (i = outLines; i > startLine; --i)
  461.             out[i + numcrs] = out[i]
  462.         outLines += numcrs
  463.         out[startLine] = substr(out[startLine], 1, ACharPos[x] - 1) lineArr[1]
  464.         for (i = 1; i < numcrs; ++i)
  465.             out[startLine + i] = lineArr[i+1]
  466.         out[startLine + numcrs] = lineArr[numcrs + 1] trailer
  467.         }
  468.     }
  469.  
  470. #Take the inserted text out - note the inserted text as recorded in the
  471. # activity record is not needed.
  472. function UndoInsert(x,         i, trailer, startLine, numcrs)
  473.     {
  474.     startLine = ALine[x]
  475.     if (ANumCRs[x] == 0)
  476.         {
  477.         out[startLine] = substr(out[startLine], 1, ACharPos[x] - 1)\
  478.             substr(out[startLine], ACharPos[x] + ASize[x]);
  479.         }
  480.     else
  481.         {
  482.         trailer = substr(out[startLine + ANumCRs[x]], ALLChars[x] + 1)
  483.         out[startLine] = substr(out[startLine], 1, ACharPos[x] - 1) trailer
  484.         numcrs = ANumCRs[x]
  485.         for (i = startLine + numcrs + 1; i <= outLines; ++i)
  486.             out[i - numcrs] = out[i]
  487.         outLines -= numcrs
  488.         }
  489.     }
  490.  
  491. # Reverted version is sent to stdout.
  492. function PrintUndoneVersionOfFile(    i)
  493.     {
  494.     for (i = 1; i < outLines; ++i)
  495.         print out[i]
  496.     printf out[outLines]
  497.     }
  498.  
  499. function Error(msg)
  500.     {
  501.     print "Error, ", msg "."
  502.     if (doingActivityFile)
  503.         print FNR, $0
  504.     exit
  505.     }
  506.  
  507. # For testing only from here to end.
  508. function DumpActivities(    i)
  509.     {
  510.     print "activity file:", activityFile
  511.     print "undo file:", undoFile
  512.     print "quoted undo name:", quotedUndoName
  513.     print "activity number to undo back to:", howFarBack
  514.     print "number of activities to undo:", numActivities
  515.     for (i = 1; i <= numActivities; ++i)
  516.         {
  517.         print "¶" AWhich[i], i
  518.         print "activity size:", ASize[i], (ADelete[i] ? "deleted" : "inserted")
  519.         print "Line:", ALine[i], "CharPos:", ACharPos[i]
  520.         print "Num CRs:", ANumCRs[i], "Last line chars:", ALLChars[i]
  521.         if (i in AText)
  522.             print "«" AText[i] "»"
  523.         print "¬"
  524.         }
  525.     exit
  526.     }
  527.  
  528. function GetActivitiesForStats()
  529.     {
  530.     while ((getline < activityFile) > 0) # "timestamp" line
  531.         {
  532.         ParseTimeLine()
  533.         GetNextActivityLine() #"<=deleted"/"=>inserted"/"¬"/other
  534.         if (match($0, /^¬/))
  535.             {
  536.             AWhich[++numActivities] = which
  537.             SeeIfAnotherActivity()
  538.             continue
  539.             }
  540.         else if (match($0, /^=>inserted/))
  541.             {
  542.             GetActivitySizes()
  543.             if (ySize != yShownSize)
  544.                 Error("activity file is missing data for an insertion")
  545.             GetNextActivityLine()
  546.             RecordActivity(0)
  547.             }
  548.         else if (match($0, /^<=deleted/))
  549.             {
  550.             GetActivitySizes()
  551.             if (ySize != yShownSize)
  552.                 Error("activity file is missing data for a deletion")
  553.             GetNextActivityLine()
  554.             RecordActivity(1)
  555.             }
  556.         else
  557.             AWhich[++numActivities] = which;
  558.         GetNextActivityLine()
  559.         if (!match($0, /^¬/))
  560.             Error("end of activity not found")
  561.         SeeIfAnotherActivity()
  562.         }
  563.     }
  564.  
  565. # Load all activities, print summary.
  566. function DumpStats()
  567.     {
  568.     howFarBack = 1
  569.     SkipEarlyActivities()
  570.     GetActivitiesForStats()
  571.     PrintActivityStats() # exits
  572.     }
  573.  
  574. # A little stats function, used during development
  575. function PrintActivityStats(    i, size, total, num)
  576.     {
  577.     for (i = 1; i <= numActivities; ++i)
  578.         {
  579.         size = ASize[i]
  580.         total += size
  581.         if (size+0 == 0)
  582.             ++bin[0]
  583.         else if (size < 500)
  584.             ++bin[1]
  585.         else if (size < 1000)
  586.             ++bin[2]
  587.         else if (size < 2000)
  588.             ++bin[3]
  589.         else if (size < 4000)
  590.             ++bin[4]
  591.         else if (size < 8000)
  592.             ++bin[5]
  593.         else if (size < 16000)
  594.             ++bin[6]
  595.         else if (size < 32000)
  596.             ++bin[7]
  597.         else if (size < 64000)
  598.             ++bin[8]
  599.         else
  600.             ++bin[9]
  601.         }
  602.     print "number of activities:", numActivities
  603.     print "total data bytes:", total
  604.     print "average data per activity:", total/numActivities
  605.     num = 500
  606.     print "Activities by size:"
  607.     print "-------------------"
  608.     print "no data:", bin[o], (bin[0]/numActivities)*100, "%"
  609.     for (i = 1; i <= 8; ++i)
  610.         {
  611.         print "under", num, ":", bin[i], (bin[i]/numActivities)*100, "%"
  612.         num *= 2
  613.         }
  614.     print "over 64000:", bin[9], (bin[9]/numActivities)*100, "%"
  615.     for (i = 1; i <= numActivities; ++i)
  616.         print "¶" AWhich[i], i;
  617.     exit
  618.     }
  619.